package net.jacqeush.mini.rpc.annotation.auto.register;

import lombok.Setter;
import org.springframework.aop.scope.ScopedProxyUtils;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionDefaults;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.annotation.*;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

import java.util.LinkedHashSet;
import java.util.Set;

/**
 * @author Jacquesh
 * @version 1.0
 * @date 2020/10/18 22:52
 */
@Setter
public class RpcBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
    private   BeanDefinitionRegistry registry;
     private    RpcBeanNameGenerator rpcBeanNameGenerator;
    private BeanDefinitionDefaults beanDefinitionDefaults = new BeanDefinitionDefaults();

    @Nullable
    private String[] autowireCandidatePatterns;

    private BeanNameGenerator beanNameGenerator = AnnotationBeanNameGenerator.INSTANCE;

    private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();

    private boolean includeAnnotationConfig = true;

    /**
     * 实体类对应的AnnotationClazz
     */

    private Class selfAnnotationClazz;

    /**
     * 传值使用的临时静态变量
     */
    private static Class staticTempAnnotationClazz = null;


    /**
     * 因构造函数无法传入指定的Annotation类，需使用静态方法来调用
     *
     */
    public static synchronized RpcBeanDefinitionScanner getScanner(BeanDefinitionRegistry registry, Class clazz) {
        staticTempAnnotationClazz = clazz;
        RpcBeanDefinitionScanner scanner = new RpcBeanDefinitionScanner(registry);
        scanner.setSelfAnnotationClazz(clazz);
        return scanner;
    }

    private RpcBeanDefinitionScanner(BeanDefinitionRegistry registry) {
        super(registry);
        this.registry=registry;
       this.rpcBeanNameGenerator =new RpcBeanNameGenerator();
    }


    @Override
    public void registerDefaultFilters() {

        this.addIncludeFilter(new AnnotationTypeFilter(staticTempAnnotationClazz));
    }

    /** 以下为初始化后调用的方法 */
    @Override
    public Set<BeanDefinitionHolder> doScan(String... basePackages) {

        Assert.notEmpty(basePackages, "At least one base package must be specified");
        Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
        for (String basePackage : basePackages) {
            Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
            for (BeanDefinition candidate : candidates) {
                ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
                candidate.setScope(scopeMetadata.getScopeName());
                String beanName = this.rpcBeanNameGenerator.generateBeanName(candidate, this.registry);
                if (candidate instanceof AbstractBeanDefinition) {
                    postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
                }
                if (candidate instanceof AnnotatedBeanDefinition) {
                    AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
                }
                if (checkCandidate(beanName, candidate)) {
                    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                   definitionHolder=this.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                    beanDefinitions.add(definitionHolder);
                    registerBeanDefinition(definitionHolder, this.registry);
                }
            }
        }
        return beanDefinitions;

    }
     BeanDefinitionHolder applyScopedProxyMode(
            ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {

        ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
        if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
            return definition;
        }
        boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
        return ScopedProxyUtils.createScopedProxy(definition, registry, proxyTargetClass);
    }

    @Override
    public boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        return super.isCandidateComponent(beanDefinition)
                && beanDefinition.getMetadata().hasAnnotation(this.selfAnnotationClazz.getName());
    }
}